package org.opentripplanner.ext.transmodelapi.mapping;

import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.TransitEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TransitIdMapper {
  private static final Logger LOG = LoggerFactory.getLogger(TransmodelMappingUtil.class);

  private static String fixedFeedId = null;

  public static String mapEntityIDToApi(TransitEntity<FeedScopedId> entity) {
    return mapIDToApi(entity.getId());
  }

  public static String mapIDToApi(FeedScopedId id) {
    if (fixedFeedId != null) {
      return id.getId();
    }
    // It is safe to use the toString(), se the JavaDoc on it
    return id.toString();
  }

  public static List<FeedScopedId> mapIDsToDomainNullSafe(Collection<String> ids) {
    return (ids == null) ? List.of() : mapIDsToDomain(ids);
  }

  public static List<FeedScopedId> mapIDsToDomain(Collection<String> ids) {
    if (ids == null) { return null; }
    List<FeedScopedId> list = new ArrayList<>();
    for (String id : ids) {
      list.add(mapIDToDomain(id));
    }
    return list;
  }

  public static FeedScopedId mapIDToDomain(String id) {
    if (id == null) {
      return null;
    }
    if (fixedFeedId != null) {
      return new FeedScopedId(fixedFeedId, id);
    }
    return FeedScopedId.parseId(id);
  }

  /**
   * This initialize the 'fixedFeedId', before this is done the GraphQL API will use the full id
   * including the feedId.
   * <p>
   * THIS CODE IS NOT THREAD SAFE!
   * <p>
   * Make sure to call this method BEFORE the server is stating-up/handling client requests.
   *
   * @param entities The entities to pick the feedId from, if more than one feedID exist, the feedId
   *                 with the most occurrences will be used. This is done to prevent a few "cases"
   *                 of wrongly set feedIds to block the entire API from working.
   * @return the fixedFeedId - used to unit test this method.
   */
  public static String setupFixedFeedId(Collection<? extends TransitEntity<FeedScopedId>> entities) {
    fixedFeedId = "UNKNOWN_FEED";

    // Count each feedId
    Map<String, Integer> feedIds = entities
        .stream()
        .map(a -> a.getId().getFeedId())
        .collect(Collectors.groupingBy(it -> it, Collectors.reducing(0, i -> 1, Integer::sum)));

    if (feedIds.isEmpty()) {
      LOG.warn("No data, unable to resolve fixedFeedScope to use in the Transmodel GraphQL API.");
    }
    else if (feedIds.size() == 1) {
      fixedFeedId = feedIds.keySet().iterator().next();
    }
    else {
      //noinspection OptionalGetWithoutIsPresent
      fixedFeedId = feedIds.entrySet().stream().max(Map.Entry.comparingByValue()).get().getKey();
      LOG.warn(
          "More than one feedId exist in the list of agencies. The feed-id used by"
          + "most agencies will be picked."
      );
    }
    LOG.info(
        "Starting Transmodel GraphQL Schema with fixed FeedId: '" + fixedFeedId
        + "'. All FeedScopedIds in API will be assumed to belong to this agency."
    );
    return fixedFeedId;
  }
}
